import bcrypt from "bcryptjs"
import { db } from "../database/connection"
import { users, userSubscriptions, pointTransactions } from "../database/schema"
import { eq, desc } from "drizzle-orm"

export class UserService {
  static async createUser(data: {
    email: string
    password?: string
    name?: string
    googleId?: string
    githubId?: string
    avatar?: string
  }) {
    const hashedPassword = data.password ? await bcrypt.hash(data.password, 12) : null

      const [user] = await db
        .insert(users)
        .values({
          email: data.email,
          password: hashedPassword,
          name: data.name,
          googleId: data.googleId,
          githubId: data.githubId,
          avatar: data.avatar,
        })
        .returning()

      // 给新用户赠送初始积分
      await this.addPoints(user.id, 100, "New user bonus")

      return user
  }

  static async findByEmail(email: string) {
    const [user] = await db.select().from(users).where(eq(users.email, email)).limit(1)
      return user || null
  }

  static async findById(id: number) {
    const [user] = await db.select().from(users).where(eq(users.id, id)).limit(1)
    return user
  }

  static async findByGoogleId(googleId: string) {
    const [user] = await db.select().from(users).where(eq(users.googleId, googleId)).limit(1)
      return user || null
  }

  static async findByGithubId(githubId: string) {
    const [user] = await db.select().from(users).where(eq(users.githubId, githubId)).limit(1)
      return user || null
  }

  static async verifyPassword(password: string, hashedPassword: string) {
    return await bcrypt.compare(password, hashedPassword)
  }

  static async updateUser(id: number, data: any) {
    const [user] = await db
        .update(users)
        .set({
          ...data,
          updatedAt: new Date(),
        })
        .where(eq(users.id, id))
        .returning()
      return user
  }

  /**
   * 修改用户密码
   * @param userId 用户ID
   * @param currentPassword 当前密码
   * @param newPassword 新密码
   * @param confirmPassword 确认密码
   * @returns 更新后的用户信息
   */
  static async changePassword(userId: number, currentPassword: string, newPassword: string, confirmPassword: string) {
    // 验证新密码和确认密码是否一致
    if (newPassword !== confirmPassword) {
      throw createError({
        statusCode: 442,
        statusMessage: 'password.mismatch'
      })
    }

    // 获取用户信息
    const user = await this.findById(userId)
    if (!user) {
      throw createError({
        statusCode: 442,
        statusMessage: 'user.notFound'
      })
    }

    const isPasswordValid = await this.verifyPassword(currentPassword, user.password)
    if (!isPasswordValid) {
      throw createError({
        statusCode: 400,
        error: true,
        statusMessage: 'ZodError',
        data: [
          {
            "origin": "string",
            "code": "password.incorrect",
            "format": "email",
            "path": [
              "email"
            ],
            "message": "Invalid email address"
          }
        ]
      })
    }

    // 哈希新密码并更新
    const hashedPassword = await bcrypt.hash(newPassword, 12)
    return this.updateUser(userId, { password: hashedPassword })
  }

  static async getUserPoints(userId: number) {
    const transactions = await db
        .select()
        .from(pointTransactions)
        .where(eq(pointTransactions.userId, userId))
        .orderBy(desc(pointTransactions.createdAt))

      const totalPoints = transactions.reduce((sum: number, t: any) => {
        return t.type === "earn" ? sum + t.points : sum - t.points
      }, 0)

      return { totalPoints, transactions }
  }

  static async addPoints(userId: number, points: number, description: string, relatedId?: string) {
    const [transaction] = await db
        .insert(pointTransactions)
        .values({
          userId,
          points,
          type: "earn",
          description,
          relatedId,
        })
        .returning()

      return transaction
  }

  static async spendPoints(userId: number, points: number, description: string, relatedId?: string) {
    const { totalPoints } = await this.getUserPoints(userId)

      if (totalPoints < points) {
        throw new Error("Insufficient points")
      }

      const [transaction] = await db
        .insert(pointTransactions)
        .values({
          userId,
          points,
          type: "spend",
          description,
          relatedId,
        })
        .returning()

      return transaction
  }

  static async getUserSubscription(userId: number) {
    const [subscription] = await db
        .select()
        .from(userSubscriptions)
        .where(eq(userSubscriptions.userId, userId))
        .orderBy(desc(userSubscriptions.createdAt))
        .limit(1)

      return subscription || null
  }

  static async isProUser(userId: number) {
    const subscription = await this.getUserSubscription(userId)

      if (!subscription) return false

      return (
        subscription.status === "active" &&
        subscription.currentPeriodEnd &&
        new Date(subscription.currentPeriodEnd) > new Date()
      )
  }
}
